Bitwise Operations
contents
비트 연산(Bitwise Operations) 에 대해 알아보겠습니다.
보통 웹 개발(Spring Boot)에서는 비즈니스 로직에 집중하기 때문에 비트 연산을 직접 쓸 일이 거의 없습니다. 하지만 다음 세 가지 경우에는 필수적입니다:
- 성능 최적화 (고빈도 매매, 게임 엔진, 이미지 처리).
- 데이터 압축 저장 (플래그, 권한 관리).
- 기술 면접 (LeetCode, 알고리즘 문제).
컴퓨터는 숫자 5나 10을 모릅니다. 오직 0과 1의 흐름만 볼 뿐입니다. 비트 연산자는 이 원시 비트들을 직접 조작할 수 있게 해줍니다.
상세 내용은 다음과 같습니다.
1. 기본 설정: 이진수 표현 (Binary Representation)
자바에서 int는 32비트 (4바이트)입니다.
- 양수: 우리가 아는 일반적인 이진수로 표현됩니다.
5=...000101
- 음수: 2의 보수(Two's Complement) 법을 사용합니다.
-5를 만드는 법:5의 비트를 전부 뒤집고(...111010) 그 결과에1을 더합니다(...111011).
2. 핵심 연산자 (The Core Operators)
A. 비트 AND (&)
규칙: 두 비트가 모두 1일 때만 1을 반환합니다.
- 사용처: 마스킹(Masking) (특정 비트만 뽑아내거나 0으로 만들 때).
int a = 5; // 0000 0101
int b = 3; // 0000 0011
int c = a & b;
// 계산:
// 0101
// & 0011
// ------
// 0001 (결과: 1)
B. 비트 OR (|)
규칙: 두 비트 중 하나라도 1이면 1을 반환합니다.
- 사용처: 설정(Setting) (특정 비트를 1로 켤 때 / 플래그 조합).
int a = 5; // 0000 0101
int b = 3; // 0000 0011
int c = a | b;
// 계산:
// 0101
// | 0011
// ------
// 0111 (결과: 7)
C. 비트 XOR (^)
규칙: 두 비트가 서로 다를 때만 1을 반환합니다. (배타적 논리합)
- 사용처: 비트 토글(Toggle) 또는 "왕따 찾기" (같은 숫자를 XOR 하면 0이 됨).
int a = 5; // 0000 0101
int b = 3; // 0000 0011
int c = a ^ b;
// 계산:
// 0101
// ^ 0011
// ------
// 0110 (결과: 6)
D. 비트 NOT (~)
규칙: 모든 비트를 뒤집습니다 (0 $\rightarrow$ 1, 1 $\rightarrow$ 0).
- 주의: 맨 앞의 "부호 비트(Sign Bit)"까지 뒤집히므로, 양수는 음수가 됩니다.
int a = 5; // 0000 ... 0101
int b = ~a;
// 결과: 1111 ... 1010 (2의 보수법에 의해 -6이 됨)
3. 시프트 연산자 (비트 이동)
A. 부호 있는 왼쪽 시프트 (<<)
규칙: 비트를 왼쪽으로 밀고, 빈 자리는 0으로 채웁니다.
- 수학적 의미: $2^n$을 곱하는 것과 같습니다.
5 << 1$\rightarrow$10($5 \times 2$)5 << 2$\rightarrow$20($5 \times 4$)
int a = 5; // 0000 0101
int b = a << 1; // 0000 1010 (10)
B. 부호 있는 오른쪽 시프트 (>>)
규칙: 비트를 오른쪽으로 밉니다. 부호 비트(Sign Bit)를 유지합니다.
- 수학적 의미: $2^n$으로 나누는 것과 같습니다 (내림).
- 양수일 때: 왼쪽 빈 자리를
0으로 채움. - 음수일 때: 왼쪽 빈 자리를
1로 채움 (음수 유지).
int a = 10; // 0000 1010
int b = a >> 1; // 0000 0101 (5)
int c = -10; // 1111 0110
int d = c >> 1; // 1111 1011 (-5)
C. 부호 없는 오른쪽 시프트 (>>>)
규칙: 비트를 오른쪽으로 밉니다. 무조건 왼쪽 빈 자리를 0으로 채웁니다.
- 사용처: "음수"라는 개념이 없는 원시 바이너리 데이터(해싱, 암호화)를 다룰 때 씁니다.
- 차이점:
>>는 음수를 음수로 유지하지만,>>>는 음수를 거대한 양수로 바꿔버립니다.
4. 실전 활용 사례 (이걸 왜 배울까요?)
A. 권한 관리 (Flags)
boolean canRead, boolean canWrite 처럼 변수를 여러 개 만드는 대신, int 하나로 관리합니다.
// 권한 정의 (2의 제곱수)
int READ = 1; // 001
int WRITE = 2; // 010
int EXEC = 4; // 100
// 권한 부여 (OR 사용)
int userPerms = READ | WRITE; // 011 (3 -> 읽기+쓰기 권한)
// 권한 확인 (AND 사용)
boolean canRead = (userPerms & READ) != 0; // True
boolean canExec = (userPerms & EXEC) != 0; // False
B. 홀수/짝수 판별 (성능)
(n % 2) 연산은 나눗셈을 하므로 상대적으로 느립니다. 마지막 비트만 확인하면 즉시 알 수 있습니다.
- 마지막 비트가
0: 짝수. - 마지막 비트가
1: 홀수.
if ((n & 1) == 0) {
System.out.println("짝수");
}
C. 변수 값 교환 (임시 변수 없이 - XOR Swap)
면접용 "자랑하기" 트릭입니다. temp 변수 없이 두 값을 바꿀 수 있습니다.
int a = 5, b = 10;
a = a ^ b;
b = a ^ b; // b는 5가 됨
a = a ^ b; // a는 10이 됨
5. 요약 테이블
| 기호 | 이름 | 설명 | 수학적 의미 |
|---|---|---|---|
& |
AND | 둘 다 1이면 1. | 교집합 (Intersection). |
| ` | ` | OR | 둘 중 하나라도 1이면 1. |
^ |
XOR | 서로 다르면 1. | 뺄셈/토글. |
~ |
NOT | 모든 비트 반전. | $(-x) - 1$ |
<< |
Left Shift | 왼쪽으로 이동, 0 채움. | $2^n$ 곱하기. |
>> |
Signed Right Shift | 오른쪽으로 이동, 부호 유지. | $2^n$ 나누기. |
>>> |
Unsigned Right Shift | 오른쪽으로 이동, 0 채움. | (해싱 등에 사용). |
references